﻿/**
 * IMonAIR: A new open-source library for instant messenger connectivity. <http://code.google.com/p/imonair/>
 *
 * This software is released under the MIT License <http://www.opensource.org/licenses/mit-license.php>
 */
/**
 * @author Andre de Groot <http://www.cmd.tech.nhl.nl/users/groot406/weblog/>
 * @author Piotr Zema <http://felixz.mark-naegeli.com>
 */
package net.imonair.msn.contacts {
	import flash.events.EventDispatcher;
	import flash.events.Event;
	import net.imonair.core.connectors.IIMConnector;
	import net.imonair.core.contacts.IIMContact;
	import net.imonair.core.contacts.IMStatus;
	import net.imonair.core.chats.IIMMessage;
	import net.imonair.msn.connectors.MSNConnector;
	import net.imonair.msn.events.MSNConnectorEvent;
	import net.imonair.msn.events.MSNContactEvent;
	import net.imonair.msn.events.MSNChatEvent;
	import net.imonair.msn.chats.MSNSwitchBoard;
	import net.imonair.msn.chats.MSNMessage;
	import net.imonair.msn.utils.simplifyStatus;
	/**
	 * Dispatched when when contact goes online.
	 * @eventType net.imonair.msn.events.MSNConnectorEvent.CONTACT_ONLINE
	 */
    [Event(name = "contact online", type = "net.imonair.msn.events.MSNContactEvent")]
	/**
	 * Dispatched when contact goes offline.
	 * @eventType net.imonair.msn.events.MSNConnectorEvent.CONTACT_OFFLINE
	 */
    [Event(name = "contact offline", type = "net.imonair.msn.events.MSNContactEvent")]
	/**
	 * Dispatched when contact's status changes.
	 * @eventType net.imonair.msn.events.MSNConnectorEvent.STATUS_CHANGE
	 */
    [Event(name = "status change", type = "net.imonair.msn.events.MSNContactEvent")]
	/**
	 * Dispatched when contact's group changes.
	 * @eventType net.imonair.msn.events.MSNConnectorEvent.GROUP_CHANGE
	 */
    [Event(name = "group change", type = "net.imonair.msn.events.MSNContactEvent")]
	/**
	 * Dispatched when any property of contact changes.
	 * @eventType net.imonair.msn.events.MSNConnectorEvent.CHANGE
	 */
    [Event(name = "change", type = "net.imonair.msn.events.MSNContactEvent")]
	/**
	 * This class represents a single contact.
	 * This also includes managing the switchboard connection with the contact.
	 * @see net.imonair.core.contacts.IIMContact
	 */
	public class MSNContact extends EventDispatcher implements IIMContact {
		private var _displayName:String, _email:String, _clientID:String, _status:String = MSNStatus.OFFLINE;
		private var _contactGroups:Array = [], _messageQueue:Array = [];
		private var _switchBoardOpen:Boolean = false;
		private var _listsData:uint, queueTrID:uint;
		private var _switchBoard:MSNSwitchBoard;
		private var _connector:MSNConnector;
		private var _msnObject:XML;
		/**
		 * Creates a new MSN contact.
		 * @param connector A related connector.
		 */
		public function MSNContact(connector:MSNConnector) {
			this._connector = connector;
		}
		/**
		 * Provides related connector.
		 */
		public function get connector():IIMConnector {
			return this._connector;
		}
        /**
         * Determines the display name of the contact, if not avaiable, returns an id.
		 * @see #id
         */
		public function get displayName():String {
			return _displayName || this.id;
		}
		public function set displayName(value:String):void {
			if (this._displayName != value) {
				this._displayName = value;
				dispatchEvent(new MSNContactEvent(MSNContactEvent.NAME_CHANGE, this));
				this._connector.dispatchEvent(new MSNContactEvent(MSNContactEvent.NAME_CHANGE, this));
				dispatchEvent(new MSNContactEvent(MSNContactEvent.CHANGE, this));
				this._connector.dispatchEvent(new MSNContactEvent(MSNContactEvent.CHANGE, this));
			}
		}
        /**
         * Determines the LiveID of the contact.
         */
		public function get id():String {
			return this._email;
		}
		public function set id(value:String):void {
			this._email = value;	
		}
        /**
         * Determines the status of the contact.
         */
		public function get status():String {
			return this._status;
		}
		public function set status(value:String):void {
			if (this._status != value) {
				this._status = value; 
				if (this._status == IMStatus.ONLINE) {
					dispatchEvent(new MSNContactEvent(MSNContactEvent.ONLINE, this));
					this._connector.dispatchEvent(new MSNContactEvent(MSNContactEvent.ONLINE, this));
				}
				if (this._status == IMStatus.OFFLINE) {
					this._switchBoardOpen = false;
					dispatchEvent(new MSNContactEvent(MSNContactEvent.OFFLINE, this));
					this._connector.dispatchEvent(new MSNContactEvent(MSNContactEvent.OFFLINE, this));
				}
				dispatchEvent(new MSNContactEvent(MSNContactEvent.STATUS_CHANGE, this));
				this._connector.dispatchEvent(new MSNContactEvent(MSNContactEvent.STATUS_CHANGE, this));
				dispatchEvent(new MSNContactEvent(MSNContactEvent.CHANGE, this));
				this._connector.dispatchEvent(new MSNContactEvent(MSNContactEvent.CHANGE, this));
			}
		}
		/**
		 * Provides simplified and unified over all networks status.
		 */
		public function get simpleStatus():String {
			return simplifyStatus(this.status);
		}
		/**
		 * Determines the groups of the contact.
		 */
		public function get contactGroups():Array {
			return this._contactGroups;
		}
		public function set contactGroups(value:Array):void {
			if (this._contactGroups != value) {
				this._contactGroups = [value];
				dispatchEvent(new MSNContactEvent(MSNContactEvent.GROUP_CHANGE, this));
				this._connector.dispatchEvent(new MSNContactEvent(MSNContactEvent.GROUP_CHANGE, this));
				dispatchEvent(new MSNContactEvent(MSNContactEvent.CHANGE, this));
				this._connector.dispatchEvent(new MSNContactEvent(MSNContactEvent.CHANGE, this));
			}
		}
		/**
		 * @private
		 */
		public function get switchBoard():MSNSwitchBoard {
			return this._switchBoard;
		}
		public function set switchBoard(value:MSNSwitchBoard):void {
			if (this._switchBoard != value)	{
				this._switchBoard = value;
				this._switchBoardOpen = true;
				dispatchEvent(new MSNContactEvent(MSNContactEvent.CHANGE, this));
				this._connector.dispatchEvent(new MSNContactEvent(MSNContactEvent.CHANGE, this));
			}
		 }
		/**
		 * @private
		 */
		public function get switchBoardOpen():Boolean {
			return this._switchBoardOpen;
		}
		/**
		 * @private
		 */
		public function get listsData():uint {
			return this._listsData;
		}
		public function set listsData(value:uint):void {
			if (!this._listsData) this._listsData = value;
		}
		/**
		 * What it is for??
		 */
		public function get clientID():String {
			return this._clientID
		}
		public function set clientID(value:String):void {
			if (this._clientID != value) {
				this._clientID = value; 
				this._connector.dispatchEvent(new MSNContactEvent(MSNContactEvent.CHANGE, this));
			}
		}
		/**
		 * The MSNObject serves as a way to identify Display Pictures, Custom Emoticons, Backgrounds, Voice Clips etc (basically any "content" except file transfers).
		 */
		public function get msnObject():XML {//TODO Implement XML
			return <msnobj Creator="buddy@hotmail.com" Size="24539" Type="3" Location="TFR2C.tmp" Friendly="AAA=" SHA1D="trC8SlFx2sWQxZMIBAWSEnXc8oQ=" SHA1C="U32o6bosZzluJq82eAtMpx5dIEI="/>;
		}
		public function set msnObject(value:XML):void {
			this._msnObject = value;
		}
		/**
		 * Sends a message to current contact.
		 * @param message A MSNMessage instance to be sent.
		 */
		public function sendMessage(message:IIMMessage):void {
			if (this._switchBoard) this._switchBoard.sendMessage(message) else {
				this._messageQueue.push(message);
				this.queueTrID = this._connector.trID;
				this._connector.sendCommand("XFR " + this.queueTrID + " SB\r\n");
				this._connector.addEventListener(MSNChatEvent.CONNECT, switchBoardConnectHandler);
			}
		}
		public function closeSwitchBoard(alsoCloseConnection:Boolean = true):void {
			if (alsoCloseConnection) _switchBoard.close();
			this._switchBoardOpen = false;
			this._switchBoard = null;
		}
		/**
		 * Checks whether a contact is on one of four lists:<li>FORWARD</li><li>ALLOW</li><li>BLOCK</li><li>REVERSE</li>
		 * @param listCode A code of a list to check.
		 * @return A response
		 */
		public function isOnList(listCode:uint):Boolean {//Not sure this will work
			return (this._listsData & listCode) as Boolean;
		}
		private function switchBoardConnectHandler(event:MSNChatEvent):void {
			event.currentTarget.removeEventListener(MSNChatEvent.CONNECT, switchBoardConnectHandler);
			if (event.switchBoard.connectTrID == this.queueTrID) {
				this._switchBoard = event.switchBoard as MSNSwitchBoard;
				this._switchBoardOpen = true;
				this._switchBoard.addEventListener(MSNChatEvent.START, switchBoardReadyHandler);
				this._switchBoard.addContact(this);
			}
		}
		private function switchBoardReadyHandler(event:MSNChatEvent):void {
			event.currentTarget.removeEventListener(MSNChatEvent.START, switchBoardConnectHandler);
			for each (var value:MSNMessage in this._messageQueue) sendMessage(value);
			this._messageQueue = [];
		}
	}
}